{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### A Simple Function Timer"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We want to create a simple function that can time how fast a function runs.\n",
    "\n",
    "We want this function to be generic in the sense that it can be used to time any function (along with it's positional and keyword arguments), as well as specifying the number of the times the function should be timed, and the returns the average of the timings."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We'll call our function **time_it**, and it will need to have the following parameters:\n",
    "\n",
    "* the function we want to time\n",
    "* the positional arguments of the function we want to time (if any)\n",
    "* the keyword-only arguments of the function we want to time (if any)\n",
    "* the number of times we want to run this function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def time_it(fn, *args, rep=5, **kwargs):\n",
    "    print(args, rep, kwargs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we could the function this way:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1, 2, 3) 5 {'sep': '-'}\n"
     ]
    }
   ],
   "source": [
    "time_it(print, 1, 2, 3, sep='-')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's modify our function to actually run the print function with any positional and keyword args (except for rep) passed to it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def time_it(fn, *args, rep=5, **kwargs):\n",
    "    for i in range(rep):\n",
    "        fn(*args, **kwargs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1-2-3\n",
      "1-2-3\n",
      "1-2-3\n",
      "1-2-3\n",
      "1-2-3\n"
     ]
    }
   ],
   "source": [
    "time_it(print, 1, 2, 3, sep='-')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see **1, 2, 3** was passed to the **print** function's positional parameters, and the keyword_only arg **sep** was also passed to it. \n",
    "\n",
    "We can even add more arguments:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1-2-3 *** 1-2-3 *** 1-2-3 *** "
     ]
    }
   ],
   "source": [
    "time_it(print, 1, 2, 3, sep='-', end=' *** ', rep=3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now all that's really left for us to do is to time the function and return the average time:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def time_it(fn, *args, rep=5, **kwargs):\n",
    "    start = time.perf_counter()\n",
    "    for i in range(rep):\n",
    "        fn(*args, **kwargs)\n",
    "    end = time.perf_counter()\n",
    "    return (end - start) / rep"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's write a few functions we might want to time:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We'll create three functions that all do the same thing: calculate powers of n**k for k in some range of integer values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def compute_powers_1(n, *, start=1, end):\n",
    "    # using a for loop\n",
    "    results = []\n",
    "    for i in range(start, end):\n",
    "        results.append(n**i)\n",
    "    return results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def compute_powers_2(n, *, start=1, end):\n",
    "    # using a list comprehension\n",
    "    return [n**i for i in range(start, end)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def compute_powers_3(n, *, start=1, end):\n",
    "    # using a generator expression\n",
    "    return (n**i for i in range(start, end))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's run these functions and see the results:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[2, 4, 8, 16]"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "compute_powers_1(2, end=5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[2, 4, 8, 16]"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "compute_powers_2(2, end=5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[2, 4, 8, 16]"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(compute_powers_3(2, end=5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally let's run these functions through our **time_it** function and see the results:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2.5798198230283234"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "time_it(compute_powers_1, n=2, end=20000, rep=4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2.3151767636341347"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "time_it(compute_powers_2, 2, end=20000, rep=4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3.0854032573301993e-06"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "time_it(compute_powers_3, 2, end=20000, rep=4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Although the **compute_powers_3** function appears to be **much** faster than the other two, it doesn't quite do the same thing! \n",
    "\n",
    "We'll cover generators in detail later in this course."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
